/**
 * 
 */
package badugi;

import java.util.Random;

/**
 * @author aleks
 *
 */
public class Room extends Thread {
	
	private int nrOfTheGame;
	private int nrOfPlayers;
	private int smallBlindValue;
	private int bigBlindValue;
	private Pack pack;
	private ServerThread[] sequence;
	private Hand[] allHands;
	private int phase = 1;
	private Random random;
	private int whoHasDealerButton;
	private boolean wePlay;
	private int[] maxBetInEachBettingRound = new int[4];
	
	/**
	 * @author aleks
	 *
	 */	
	Room(int nrOfPlayers, int nrOfTokens, int smallBlindValue, int bigBlindValue) throws RoomException {
		if (nrOfPlayers < 2 || nrOfPlayers > 6) throw new RoomException(0);
		if (nrOfTokens < smallBlindValue || nrOfTokens < bigBlindValue) throw new RoomException(1);
		if (smallBlindValue >= bigBlindValue) throw new RoomException(2);
		this.nrOfPlayers = nrOfPlayers;
		this.smallBlindValue = smallBlindValue;
		this.bigBlindValue = bigBlindValue;
		pack = new Pack();
		random = new Random();
		nrOfTheGame = 1;
		wePlay = true;
		for (int i = 0; i < maxBetInEachBettingRound.length; i++) { maxBetInEachBettingRound[i] = 0; }
	}
	
	public void setSequence(ServerThread[] seq) {
		this.sequence = seq;
	}
	
	public void run() {
		System.out.println("Room was started");
		canWeStart();
		if (!isAllThreadsAlive()) {
			System.out.println("Some of players is disconnected.");
			sendMessageToAll("<" + nrOfPlayers + "><30><02>");
			wePlay = false;
		}
		else {
			System.out.println("Yes, we can start now.");
		}
		while (wePlay) {
			if (!isAllRight()) {
				sendMessageToAll("player id log out or hax no cash");
				phase = -1;
				break;
			}
			if (phase == 1) {
				System.out.println("MATCH NR " + nrOfTheGame + "\nPhase 1: preparing to game.");
				pack.shufflePack();
				if (nrOfTheGame == 1) {
					whoHasDealerButton = random.nextInt(nrOfPlayers);
				}
				else {
					whoHasDealerButton = (whoHasDealerButton + 1) % nrOfPlayers;
				}
				
				sendMessageToAll("<" + nrOfPlayers + "><04><" + whoHasDealerButton + "," + (int)( (whoHasDealerButton+1) % nrOfPlayers ) + "," + (int)( (whoHasDealerButton+2) % nrOfPlayers ) + ">");
				allHands = new Hand[nrOfPlayers];
				for (int i = 0; i < allHands.length; i++) {
					allHands[i] = new Hand(pack.giveMeOneCard(), pack.giveMeOneCard(), pack.giveMeOneCard(), pack.giveMeOneCard());
				}
				synchronized(this) {
					for (int i = 0; i < sequence.length; i++) {
						if (sequence[i] != null) {
							sequence[i].sendToOut("<" + nrOfPlayers + "><03><" + allHands[i].toString() + ">");
							System.out.println("I sent: " + "<" + nrOfPlayers + "><03><" + allHands[i].toString() + ">");
						}
					}
				}
				phase = 2;
				System.out.println("End of phase 1.");
			}
			if (phase == 2) {
				try {
					System.out.println("Phase 2: The first betting round.");
					phase = bettingRound(0);
					System.out.println("End of phase 2. Let's go to the phase " + phase);
				}
				catch (Exception e) {
					e.getMessage();
				}
			}
			if (phase == 3) {
				try {
					System.out.println("Phase 3: The first changing round.");
					changeRound();
					phase = 4;
					System.out.println("End of phase 3.");
				}
				catch (Exception e) {
					e.getMessage();
				}
			}
			if (phase == 4) {
				try {
					System.out.println("Phase 4: The second betting round.");
					phase = bettingRound(1);
					System.out.println("End of phase 4.");
				}
				catch (Exception e) {
					e.getMessage();
				}
			}
			if (phase == 5) {
				try {
					System.out.println("Phase 5: The secound changing round.");
					changeRound();
					phase = 6;
					System.out.println("End of phase 5.");
				}
				catch (Exception e) {
					e.getMessage();
				}
			}
			if (phase == 6) {
				try {
					System.out.println("Phase 6: The third betting round.");
					phase = bettingRound(2);
					System.out.println("End of phase 6.");
				}
				catch (Exception e) {
					e.getMessage();
				}
			}
			if (phase == 7) {
				try {
					System.out.println("Phase 7: The third changing round.");
					changeRound();
					phase = 8;
					System.out.println("End of phase 7.");
				}
				catch (Exception e) {
					e.getMessage();
				}
			}
			if (phase == 8) {
				try {
					System.out.println("Phase 4: The fourth betting round.");
					phase = bettingRound(3);
					System.out.println("End of phase 8.");
				}
				catch (Exception e) {
					e.getMessage();
				}
			}
			if (phase == 9) {
				System.out.println("Phase 9: who win.");
				int nrOfPlayerLeft = 0;
				int whoWon = 0;
				for (int i = 0; i < nrOfPlayers; i++) {
					if (sequence[i].isInTheGame() || sequence[i].isAllIn()) nrOfPlayerLeft++;
				}
				System.out.println("Left: " + nrOfPlayerLeft + " left.");
				if (nrOfPlayerLeft == 1) {
					for (int i = 0; i < nrOfPlayers; i++) {
						if (sequence[i].isInTheGame() || sequence[i].isAllIn()) whoWon = i;
					}
				}
				else {
					Hand[] theLastHands = new Hand[nrOfPlayerLeft];
					int k = 0;
					for (int i = 0; i < nrOfPlayers; i++) {
						if (sequence[i].isInTheGame() || sequence[i].isAllIn()) {
							theLastHands[k] = allHands[i];
							k = k + 1;
						}
					}
	 				whoWon = WhoWon.CheckWhoWon(theLastHands);
				}
				int howMuchHeWon = 0;
				if (whoWon == 0 || whoWon == 1 || whoWon == 2 || whoWon == 3 || whoWon == 4 || whoWon == 5) {
					for (int i = 0; i < nrOfPlayers; i++) {
						howMuchHeWon += sequence[i].allBettedMoney();
					}
					if (sequence[whoWon].isAllIn()) {
						int moneyBettedByWinner = sequence[whoWon].allBettedMoney();
						int moneyForWinnerAllIn = 0;
						int temp = 0;
						for (int i = 0; i < nrOfPlayers; i++) {
							temp = sequence[i].allBettedMoney() - moneyBettedByWinner;
							if (temp < 0) {
								moneyForWinnerAllIn += sequence[i].allBettedMoney();
							}
							else {
								moneyForWinnerAllIn += sequence[whoWon].allBettedMoney();
								sequence[i].takePrize(temp);
								sendMessageToID(i, "<" + nrOfPlayers + "><09><RE," + temp + ">");
							}
						}
						sequence[whoWon].takePrize(moneyForWinnerAllIn);
						sendMessageToAll("<" + nrOfPlayers + "><09><WI," + whoWon + "," + moneyForWinnerAllIn + ">");
						System.out.println("Won the player " + whoWon + " (by All-in) and he takes " + moneyForWinnerAllIn);
					}
					else {
						sequence[whoWon].takePrize(howMuchHeWon);
						sendMessageToAll("<" + nrOfPlayers + "><09><WI," + whoWon + "," + howMuchHeWon + ">");
						System.out.println("Won the player " + whoWon + " and he takes " + howMuchHeWon);
					}
				}
				//else, it means was a draw
				//TODO what if all-in player win
				else {
					String tempString = Integer.toString(whoWon);
					int[] whoIsInDraw = new int[tempString.length()];
					for (int i = 0; i < tempString.length(); i++) {
						try {
							whoIsInDraw[i] = Integer.parseInt(tempString.substring(i, i+1));
						}
						catch (NumberFormatException e) {
							
						}
					}
					for (int i = 0; i < nrOfPlayers; i++) {
						howMuchHeWon += sequence[i].allBettedMoney();
					}
					for (int i = 0; i < whoIsInDraw.length; i++) {
						sequence[i].takePrize( howMuchHeWon/whoIsInDraw.length );
					}
					sendMessageToAll("<" + nrOfPlayers + "><09><DR," + tempString + "," + (howMuchHeWon/whoIsInDraw.length) + ">");
					System.out.println("Is a draw between players: " + tempString + " and each of them takes " + (howMuchHeWon/whoIsInDraw.length));
						
				}
				phase = 10;
			}
			if (phase == 10) {
				wePlay = areWePlayingNextMatch();
				if (wePlay) {
					for (int i = 0; i < nrOfPlayers; i++) {
						sequence[i].prepareToTheNextMatch();
					}
					nrOfTheGame++;
					pack = new Pack();
					for (int i = 0; i < maxBetInEachBettingRound.length; i++) { maxBetInEachBettingRound[i] = 0; }
					phase = 1;
				}
			}
			else {
				
			}
			
		}
		System.out.println("Room quit.");
	}
	
	private boolean areWePlayingNextMatch() {
		System.out.println("I'm asking players if they want to play next match...");
		for (int i = 0; i < nrOfPlayers; i++) {
			sequence[i].setStatusInAction("waiting");
		}
		sendMessageToAll("<" + nrOfPlayers + "><10><AR>");
		do {
			
		}
		while (!areThreadsReady() && isAllThreadsAlive());
		System.out.println("I've asked.");
		for (int i = 0; i < nrOfPlayers; i++) {
			if ( "N".equals(sequence[i].getData()) || !sequence[i].isAlive() ) {
				sequence[i].sayThatPlayerDontWantToPlay();
				sendMessageToAll("<" + nrOfPlayers + "><10><SN>");
				return false;
			}
			if ( "Y".equals(sequence[i].getData()) && sequence[i].getNrOfTokens() <= 0) {
				//Sb wants to play next match, but he has no cash
				sendMessageToAll("<" + nrOfPlayers + "><10><LO," + i + ">");
				System.out.println("Player " + i + " have lost. We can't play with him.");
				return false;
			}
		}
		return true;
	}
	
	private void canWeStart() {
		synchronized (this) {
			System.out.println("I'm asking players if we can start...");
			for (int i = 0; i < nrOfPlayers; i++) {
				sequence[i].setStatusInAction("waiting");
			}
			sendMessageToAll("<" + nrOfPlayers + "><02><ST,0>");
			do {
				
			}
			while (!areThreadsReady() && isAllThreadsAlive());
			System.out.println("I've checked if we can start.");
			for (int i = 0; i < nrOfPlayers; i++) {
				sequence[i].setStatusInAction("notReady");
			}
		}
	}
	
	private boolean isAllThreadsAlive() {
		for (int i = 0; i < nrOfPlayers; i++) {
			if (!sequence[i].isAlive()) return false;
		}
		return true;
	}
	
	private void changeTheCard(int IDOfThePlayer, int indexOfChangedCard) throws Exception {
		Card oldCard = allHands[IDOfThePlayer].giveMeTheCardToThrowOut(indexOfChangedCard);
		Card newCard = pack.changeOneCard(oldCard);
		allHands[IDOfThePlayer].addNewCardFromTheChange(indexOfChangedCard, newCard);
	}
	
	private void changeRound() throws Exception {
		//int counter;
		int value;
		String status;
		//counter = whoHasDealerButton;
		sendMessageToAll("<" + nrOfPlayers + "><07><ST>");
		System.out.println("Let's start changing cards.");
		for (int counter = 0; counter < nrOfPlayers; counter++) {
			//wykonuj tylko jeśli gracz jest w grze; inaczej po prostu przejdź do kolejnego
			if (sequence[counter].isInTheGame() || sequence[counter].isAllIn()) {
				sequence[counter].setStatusInAction("waiting");
				sendMessageToID(counter, "<" + nrOfPlayers + "><07><WD>");
				System.out.println("I'm waiting for the player " + counter);
				do {
				}
				while ("waiting".equals(sequence[counter].getStatusInAction()) && sequence[counter].isAlive());
				System.out.println("I've recieved an answer from player " + counter );
				status = sequence[counter].getWhatIDo();
				if (status.equals("0") ) {
					sendMessageToAll("<" + nrOfPlayers + "><07><0," + counter + ">");
					System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><07><0," + counter + ">");
				}
				else if (status.equals("1") ) {
					value = Integer.parseInt(sequence[counter].getData());
					changeTheCard(counter, value);
					sendMessageToID(counter, "<" + nrOfPlayers + "><03><" + allHands[counter].toString() + ">");
					sendMessageToAll("<" + nrOfPlayers + "><07><1," + counter + ">");
					System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><07><1," + counter + ">");
				}
				else if (status.equals("2") ) {
					value = Integer.parseInt(sequence[counter].getData().substring(0, 1));
					changeTheCard(counter, value);
					value = Integer.parseInt(sequence[counter].getData().substring(1, 2));
					changeTheCard(counter, value);
					sendMessageToID(counter, "<" + nrOfPlayers + "><03><" + allHands[counter].toString() + ">");
					sendMessageToAll("<" + nrOfPlayers + "><07><2," + counter + ">");
					System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><07><2," + counter + ">");
				}
				else if (status.equals("3") ) {
					value = Integer.parseInt(sequence[counter].getData().substring(0, 1));
					changeTheCard(counter, value);
					value = Integer.parseInt(sequence[counter].getData().substring(1, 2));
					changeTheCard(counter, value);
					value = Integer.parseInt(sequence[counter].getData().substring(2, 3));
					changeTheCard(counter, value);
					sendMessageToID(counter, "<" + nrOfPlayers + "><03><" + allHands[counter].toString() + ">");
					sendMessageToAll("<" + nrOfPlayers + "><07><3," + counter + ">");
					System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><07><3," + counter + ">");
				}
				else if (status.equals("4") ) {
					for (int i = 0; i < 4; i++) {
						changeTheCard(counter, i);
					}
					sendMessageToID(counter, "<" + nrOfPlayers + "><03><" + allHands[counter].toString() + ">");
					sendMessageToAll("<" + nrOfPlayers + "><07><4," + counter + ">");
					System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><07><4," + counter + ">");
				}
				else {
					throw new RoomException(4);
				}
			}
		}
	}
	
	private boolean isAllRight() {
		for (int i = 0; i < nrOfPlayers; i++) {
			if (sequence[i] == null) return false;
			if (sequence[i].getNrOfTokens() < 0) return false;
			if (!sequence[i].isAlive()) return false;
		}
		return true;
	}
	
	private void sendMessageToAll(String content) {
		synchronized(this) {
			for (int i = 0; i < sequence.length; i++) {
				if (sequence[i] != null) {
					sequence[i].sendToOut(content);
				}
			}
		}
	}
	
	private void sendMessageToID(int id, String content) {
		synchronized(this) {
			if (sequence[id] != null) {
				sequence[id].sendToOut(content);
			}
		}
	}
	
	private boolean areThreadsReady() {
		synchronized (this) {
			for (int i = 0; i < sequence.length; i++) {
				if ( (sequence[i].isInTheGame() && !"ready".equals(sequence[i].checkStatus())) ) return false;
			}
			return true;
		}
	}
	
	private int bettingRound(int whichBettingRoundIs) throws Exception {
		System.out.println("Let's start betting round.");
		int playerWithSmallBlind = (whoHasDealerButton+1) % nrOfPlayers;
		int playerWithBigBlind = (whoHasDealerButton+2) % nrOfPlayers;
		int whoStarts;
		for (int i = 0; i < nrOfPlayers; i++) {
			sequence[i].setWhatCanIDo("FOCHBE");
		}
			if (whichBettingRoundIs == 0) {
				betSomeTokens(playerWithSmallBlind, smallBlindValue, whichBettingRoundIs);
				betSomeTokens(playerWithBigBlind, bigBlindValue, whichBettingRoundIs);
				for (int i = 0; i < nrOfPlayers; i++) {
					if (i != playerWithBigBlind) {
						sequence[i].setWhatCanIDo("FORACAAL");
					}
					else if (i == playerWithBigBlind) {
						sequence[i].setWhatCanIDo("FOCHRAAL");
					}
				}
				//sequence[playerWithBigBlind].setStatusInAction("ready");
				whoStarts = (playerWithBigBlind + 1) % nrOfPlayers;
			}
			else {
				whoStarts = playerWithSmallBlind;
			}
			sendMessageToAll("<" + nrOfPlayers + "><05><ST," + whichBettingRoundIs + ">");
			int counter;
			int value;
			String status;
			if (whichBettingRoundIs == 0) {
				System.out.println("Player " + whoHasDealerButton + " has dealer button.");
				System.out.println("Player " + playerWithSmallBlind + " has small blind now.");
				System.out.println("Player " + playerWithBigBlind + " has big blind now.");
			}
			System.out.println("Player " + whoStarts + " starts now.");
			do {
				counter = whoStarts;
				do {
					//wykonuj tylko jeśli gracz jest w grze; inaczej po prostu przejdź do kolejnego
					if (sequence[counter].isInTheGame()) {
						//jeśli jest ready, to go już nie kłopoczemy
						//TODO możliwe złączenie dwóch ifów
						if (!"ready".equals(sequence[counter].checkStatus()) ) {
							sequence[counter].setStatusInAction("waiting");
							sendMessageToID(counter, "<" + nrOfPlayers + "><05><WD," + whichBettingRoundIs + ">");
							System.out.println("I've asked player " + counter + " what he does...");
							do {
							}
							while ("waiting".equals(sequence[counter].getStatusInAction()) && sequence[counter].isAlive());
							status = sequence[counter].getWhatIDo();
							System.out.println("I've recieved an answer from player " + counter );
							if ( status.equals("FO") ) {
								sequence[counter].setInTheGame(false);
								sequence[counter].setStatusInAction("ready");
								sendMessageToAll("<" + nrOfPlayers + "><05><FO," + counter + "," + sequence[counter].getNrOfTokens() + ">");
								System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><05><FO," + counter + "," + sequence[counter].getNrOfTokens() + ">");
							}
							else if ( status.equals("CH") ) {
								sequence[counter].setStatusInAction("ready");
								sendMessageToAll("<" + nrOfPlayers + "><05><CH," + counter + "," + sequence[counter].getNrOfTokens() + ">");
								System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><05><CH," + counter + "," + sequence[counter].getNrOfTokens() +">");
							}
							else if ( status.equals("BE") ) {
								sequence[counter].setStatusInAction("ready");
								value = Integer.parseInt(sequence[counter].getData());
								betSomeTokens(counter, value, whichBettingRoundIs);
								for (int i = 0; i < nrOfPlayers; i++) {
									sequence[i].setWhatCanIDo("FORACAAL");
								}
								sendMessageToAll("<" + nrOfPlayers + "><05><BE," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
								System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><05><BE," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
							}
							else if ( status.equals("RA") ) {
								sequence[counter].setStatusInAction("ready");
								value = Integer.parseInt(sequence[counter].getData());
								betSomeTokens(counter, value, whichBettingRoundIs);
								for (int i = 0; i < nrOfPlayers; i++) {
									sequence[i].setWhatCanIDo("FORACAAL");
								}
								sendMessageToAll("<" + nrOfPlayers + "><05><RA," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
								System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><05><RA," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
							}
							else if ( status.equals("CA") ) {
								sequence[counter].setStatusInAction("ready");
								value = Integer.parseInt(sequence[counter].getData());
								betSomeTokens(counter, value, whichBettingRoundIs);
								sendMessageToAll("<" + nrOfPlayers + "><05><CA," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
								System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><05><CA," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
							}
							else if ( status.equals("AL") ) {
								sequence[counter].setInTheGame(false);
								sequence[counter].setStatusInAction("ready");
								sequence[counter].setAllIn(true, whichBettingRoundIs);
								betSomeTokens(counter, sequence[counter].getNrOfTokens(), whichBettingRoundIs);
								sendMessageToAll("<" + nrOfPlayers + "><05><AL," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
								System.out.println("I've sent to all: " + "<" + nrOfPlayers + "><05><AL," + counter + "," + sequence[counter].getData() + "," + sequence[counter].getNrOfTokens() + ">");
							}
							else {
								throw new RoomException(4);
							}
							//----
							if (status.equals("BE") || status.equals("RA") ) {
								synchronized(this) {
									for (int i = 0; i < sequence.length; i++) {
										if (sequence[i] != null && sequence[counter].isInTheGame() && sequence[i] != sequence[counter]) {
											sequence[i].setStatusInAction("notReady");
										}
									}
								}
							}

						}
					}
					//kolejny odpytywany gracz
					counter = (counter + 1) % nrOfPlayers;
				}
				while (counter != whoStarts);
			}
			while (!areThreadsReady());
			int countWhoWillPlay = 0;
			for (int i = 0; i < sequence.length; i++) {
				if (sequence[i] != null && sequence[counter].isInTheGame() ) {
					countWhoWillPlay++;
				}
			}
			if (countWhoWillPlay > 1) {
				if (whichBettingRoundIs == 0 ) {
					return 3;
				}
				else if (whichBettingRoundIs == 1) {
					return 5;
				}
				else if (whichBettingRoundIs == 2) {
					return 7;
				}
				else if (whichBettingRoundIs == 3) {
					return 9;
				}
				else return 9;
 			}
			else return 9;
	}

	private void betSomeTokens(int id, int value, int whichBettingRoundIs) throws Exception {
		synchronized(this) {
			sequence[id].setTokensInBet(value, whichBettingRoundIs);
			if (sequence[id].getTokensInBet(whichBettingRoundIs) > maxBetInEachBettingRound[whichBettingRoundIs]) {
				maxBetInEachBettingRound[whichBettingRoundIs] = sequence[id].getTokensInBet(whichBettingRoundIs);
			}
		}
	}

}
